/* 
 * Contributors: Youngjae Kim (youkim@cse.psu.edu)
 *               Aayush Gupta (axg354@cse.psu.edu)
 * 
 * In case if you have any doubts or questions, kindly write to: youkim@cse.psu.edu 
 *   
 * This source code provides page-level FTL scheme. 
 * 
 * Acknowledgement: We thank Jeong Uk Kang by sharing the initial version 
 * of sector-level FTL source code. 
 * 
 */

#include <stdlib.h>
#include <string.h>
#include "flash_api.h"
#include "flash.h"
#include "global.h"

ST_NAND_GI_INFO *g_pstNandGi;   // flash 全局变量控制指针

int MIN_ERASE;

/**
 * @Description: 初始化复位NAND统计值 
 * @Param:  ST_NAND_STAT_INFO *pstNandStatInfo:NAND 操作统计计数控制全局变量指针
 * @Return: void
 * @Author: zhoujie
 * @Date: 2020-08-15 00:25:02
 * @Note: 
 */
void flashNandStatReset(ST_NAND_STAT_INFO *pstNandStatInfo)
{
  pstNandStatInfo->NandEraseCnt = 0;
  pstNandStatInfo->NandWriteCnt = 0;
  pstNandStatInfo->NandReadCnt = 0;

  pstNandStatInfo->NandOOBReadCnt = 0;
  pstNandStatInfo->NandOOBWriteCnt = 0;

  pstNandStatInfo->NandGCReadCnt = 0;
  pstNandStatInfo->NandGCWriteCnt = 0 ;
  return;
}

/**
 * @Description:统计Nand读写操作 
 * @Param: enOption:Nand的读写操作
 * @Return: void
 * @Author: zhoujie
 * @Date: 2020-08-15 00:37:15
 * @Note: 
 */
void Flash_NandStat(EN_NAND_OPERATION enOption)
{
  ST_NAND_STAT_INFO *pstNandStatInfo = g_pstNandGi->pstNandBlkInfo;
  switch(enOption) {
    case EN_NAND_PAGE_READ:
      pstNandStatInfo->NandReadCnt++;
      break;
    case EN_NAND_PAGE_WRITE:
      pstNandStatInfo->NandWriteCnt++;
      break;
    case EN_NAND_BLOCK_ERASE:
      pstNandStatInfo->NandEraseCnt++;
      break;
    case EN_NAND_OOB_READ:
      pstNandStatInfo->NandOOBReadCnt++;
      break;
    case EN_NAND_OOB_WRITE:
      pstNandStatInfo->NandOOBWriteCnt++;
      break;
    case EN_GC_PAGE_READ:
      pstNandStatInfo->NandGCReadCnt++;
      break;
    case EN_GC_PAGE_WRITE:
      pstNandStatInfo->NandGCWriteCnt++;
      break;
    default:
      fprintf(stderr, "the option input is error\n");
      break;
  }
  return;
}

/**
 * @Description: 将nand的统计操作结果输出打印
 * @Param: outFP统计结果文件
 * @Return: void
 * @Author: zhoujie
 * @Date: 2020-08-15 00:54:14
 * @Note: 
 */
void Flash_NandStatPrint(FILE *outFP)
{
  ST_NAND_STAT_INFO *pstNandStatInfo = g_pstNandGi->pstNandBlkInfo;
  fprintf(outFP, "\n");
  fprintf(outFP, "FLASH STATISTICS\n");
  fprintf(outFP, "------------------------------------------------------------\n");
  fprintf(outFP, " Page read (#): %8u   ", pstNandStatInfo->NandReadCnt);
  fprintf(outFP, " Page write (#): %8u   ", pstNandStatInfo->NandGCWriteCnt);
  fprintf(outFP, " Block erase (#): %8u\n", pstNandStatInfo->NandEraseCnt);
  fprintf(outFP, " OOREAD  %8u   ", pstNandStatInfo->NandOOBReadCnt);
  fprintf(outFP, " OOWRITE %8u\n", pstNandStatInfo->NandOOBWriteCnt);
  fprintf(outFP, " GC page read (#): %8u   ", pstNandStatInfo->NandGCWriteCnt);
  fprintf(outFP, " GC page write (#): %8u\n", pstNandStatInfo->NandGCWriteCnt);
  fprintf(outFP, "------------------------------------------------------------\n");
  return;
}

/**
 * @Description: 初始化nand各个块的状态
 * @Param:   ST_NAND_GI_INFO *pstNandGi: Nand全局变量控制指针
 * @Return:  void
 * @Author: zhoujie
 * @Date: 2020-08-16 12:18:03
 * @Note: 
 */
void flashNandBlkInit(ST_NAND_GI_INFO *pstNandGi)
{
  U32 idx;
  U32 iidx;
  U32 allBlkNum = pstNandGi->allBlkNum;

  for (idx = 0; idx <allBlkNum ; idx++) {
    // 初始化块的状态
    pstNandGi->pstNandBlkInfo[idx].state.free = 1;
    pstNandGi->pstNandBlkInfo[idx].state.ec = 0;
    pstNandGi->pstNandBlkInfo[idx].freePageCnt = SECT_NUM_PER_BLK;
    pstNandGi->pstNandBlkInfo[idx].invalidPageCnt = 0;
    pstNandGi->pstNandBlkInfo[idx].lastWritePageIdx = -1;

    // 初始化块内各扇区的状态
    for (iidx = 0; iidx < SECT_NUM_PER_BLK; iidx++) {
      pstNandGi->pstNandBlkInfo[idx].sect[iidx].free = TRUE;
      pstNandGi->pstNandBlkInfo[idx].sect[iidx].valid = FALSE;
      pstNandGi->pstNandBlkInfo[idx].sect[iidx].lsn = -1;
    }

    // 初始化块内各Page的状态 (0:data, 1:map table)
    for (iidx = 0; iidx < PAGE_NUM_PER_BLK; iidx++) {
      pstNandGi->pstNandBlkInfo[idx].pageStatus[iidx] = -1;
    }
  }

  return;
}
/**
 * @Description: 初始化nand全局变量
 * @Param: AllBlkNum:全盘块个数
 *         minFreeBlkNum:最小的块个数
 * @Return: ERROR（异常）/SUCCESS（函数正常）
 * @Author: zhoujie
 * @Date: 2020-08-14 23:43:30
 * @Note: 
 */
U08 Flash_NandGIInit(U32 AllBlkNum, U08 minFreeBlkNum)
{
  g_pstNandGi = (ST_NAND_GI_INFO *)malloc(sizeof (ST_NAND_GI_INFO));
  if (g_pstNandGi == NULL) {
    return ERROR;
  }
  g_pstNandGi->pstNandStatInfo = (ST_NAND_BLK_INFO *)malloc(sizeof(ST_NAND_BLK_INFO) * AllBlkNum);
  if (g_pstNandGi->pstNandBlkInfo == NULL) {
    return ERROR;
  }
  g_pstNandGi->pstNandStatInfo = (ST_NAND_STAT_INFO *)malloc(sizeof(ST_NAND_STAT_INFO));
  if (g_pstNandGi->pstNandStatInfo == NULL) {
    return ERROR;
  }
  g_pstNandGi->minBlkNum = minFreeBlkNum;
  g_pstNandGi->minBlkNum = AllBlkNum;
  g_pstNandGi->freeBlkNum = AllBlkNum;
  g_pstNandGi->curBlkIdx = 0;
  // 初始化flash操作计数
  flashNandStatReset(g_pstNandGi->pstNandStatInfo);
  // 初始化flash Nand 各block的状态
  flashNandBlkInit(g_pstNandGi);

  return SUCCESS;
}

/**
 * @Description: 释放全局变量内存
 * @Param: void
 * @Return: void 
 * @Author: zhoujie
 * @Date: 2020-08-15 00:05:13
 * @Note: 
 */
void Flash_EndAndClear(void)
{
  if (g_pstNandGi == NULL) {
    fprintf(stderr, "g_pstNandGi is NULL\n");
    return;
  }
  free(g_pstNandGi->pstNandBlkInfo);
  g_pstNandGi->pstNandBlkInfo = NULL;
  free(g_pstNandGi->pstNandStatInfo);
  g_pstNandGi->pstNandStatInfo = NULL;
  free(g_pstNandGi);
  g_pstNandGi = NULL;
  fprintf(stdout, "Nand GI memeroy is free\n");
  return;
}

/**
 * @Description: 读取NAND BLK中 SECT 记录OOB判断数据是否有效
 * @Param:  U32 phySectAddr :physical Sect Address
 * @Return: res :0(空白页)；1(有效数据)；-1(无效数据)
 * @Author: zhoujie
 * @Date: 2020-08-16 13:29:55
 * @Note: 
 */
S16 Flash_NandOOBRead(U32 phySectAddr)
{
  U16 idx;
  // physical block number
  U32 phyBlkNum = BLK_F_SECT(phySectAddr);   
  // page index (within the block), here page index is the same as sector index
  U16 pageOffSet = IND_F_SECT(phySectAddr);  
  ST_NAND_BLK_INFO *pstNandBlk = NULL;

  // physical block number shouldn't exceed max nand block number
  ASSERT (phyBlkNum < g_pstNandGi->allBlkNum); 
  pstNandBlk = &g_pstNandGi->pstNandBlkInfo[phyBlkNum];
  // 统计Nand的操作
  Flash_NandStat(EN_NAND_OOB_READ);
  
  // 遍历对应的page 内部所有的sect 状态判断数据是否存在，且有效
  for (idx = pageOffSet; idx < (pageOffSet + SECT_NUM_PER_BLK); idx++) {
    if (pstNandBlk->sect[idx].free == FALSE) {
      // 有数据写入，数据有效
      if (pstNandBlk->sect[idx].valid == TRUE) {
        return 1;
      } else {
      // 无效数据
        return -1;
      }
    } else {
      // 无数据写入，则为空白页
      return 0;
    }
  }
}

/**
 * @Description: 模拟数据读取，Nand数组里存放的是“数据“实际上是对应的LBA，通过从MAP中获取的
 *               映射LBA生成的数据数组 和 NAND数组中存放的LBA是否一致，来判断是读取是否成功
 * @Param: U32 *lsnsArr:需要比对的LBA数组，数组长度 默认是SECT_NUM_PER_PAGE
 *         U16 pageNum: page 号（Sect 号在Blk中 偏移对齐点）
 *         ST_NAND_BLK_INFO *pstNandBlk ：数据比对块的控制结构体指针
 *         U32 phyBlkNum:对应的BLK物理块号
 * @Return: validSectCnt:比对成功的有效Sect数
 * @Author: zhoujie
 * @Date: 2020-08-16 14:37:41
 * @Note: 
 */
U08 flashNormalRead(U32 *lsnsArr, U16 pageNum, ST_NAND_BLK_INFO *pstNandBlk, U32 phyBlkNum) 
{
  U08 idx;
  U08 validSectCnt = 0;  // 对比成功的扇区个数

  // 遍历对应Page中的各扇区存放的LBA是否与MAP记录的一致，即数据比对成功
  for (idx = 0; idx < SECT_NUM_PER_PAGE; idx++) {
    if (lsnsArr[idx] != -1) {
      ASSERT(pstNandBlk->sect[idx + pageNum].free == FALSE); // 确保数据写入
      ASSERT(pstNandBlk->sect[idx + pageNum].valid == TRUE); // 确保数据有效
      ASSERT(pstNandBlk->sect[idx + pageNum].lsn == lsnsArr[idx]); //确保数据比对一致
      validSectCnt++;
    } else {
      // MAP中获取的LBA同时需要保证必须有效
      fprintf(stderr, "BLK : %d lsnsArr[%d]: %d shouldn't be -1\n", phyBlkNum, idx, lsnsArr[idx]);
      ASSERT(0);
    }
  }
  return validSectCnt;
}

/**
 * @Description: 将Nand数组中存放的“数据”(LBA),返回给MAP
 * @param: U32 *lsnsArr:待加载LBA数组，数组长度 默认是SECT_NUM_PER_PAGE
 *         U16 pageNum: page 号（Sect 号在Blk中 偏移对齐点）
 *         ST_NAND_BLK_INFO *pstNandBlk ：数据比对块的控制结构体指针
 *         U32 phyBlkNum:对应的BLK物理块号
 * @return: validSectCnt:比对加载的Sect数
 * @Author: zhoujie
 * @Date: 2020-08-16 23:12:47
 * @Note: 
 */
U08 flashGcRead(U32 *lsnsArr, U16 pageNum, ST_NAND_BLK_INFO *pstNandBlk, U32 phyBlkNum)
{
  U08 idx;
  U08 validSectCnt = 0;

  // 遍历读取Page中的各扇区存放的LBA，加载到lsns数组中
  for (idx = 0; idx < SECT_NUM_PER_PAGE; idx++) {
    // 确保Nand记录中的数据有效
    if ((pstNandBlk->sect[pageNum + idx].free == FALSE) &&
        (pstNandBlk->sect[pageNum + idx].valid == TRUE)) {
          lsnsArr[validSectCnt++] = pstNandBlk->sect[pageNum + idx].lsn;
    }
  }

  // debug print 确保lsns中的数据全部加载满
  if (validSectCnt < SECT_NUM_PER_PAGE) {
    for (idx = 0; idx < SECT_NUM_PER_PAGE; idx++) {
      fprintf(stderr, "Blk: %d, page %d: Sect:%d, free: %d, valid: %d\n", phyBlkNum, pageNum, 
              pageNum + idx, pstNandBlk->sect[pageNum + idx].free, pstNandBlk->sect[pageNum + idx].valid);
    }
    ASSERT(0);
  }

  return validSectCnt;
}

/**
 * @Description: 格式化掉NAND中不存在，但MAP中存在的LBA记录
 * @param: U32 *lsnsArr:待加载LBA数组，数组长度 默认是SECT_NUM_PER_PAGE
 *         U16 pageNum: page 号（Sect 号在Blk中 偏移对齐点）
 *         ST_NAND_BLK_INFO *pstNandBlk ：数据比对块的控制结构体指针
 *         U32 phyBlkNum:对应的BLK物理块号
 * @return:  validSectCnt:比对成功的有效Sect数
 * @Author: zhoujie
 * @Date: 2020-08-16 23:39:57
 * @Note: 
 */
U08 flashFormatRead(U32 *lsnsArr, U16 pageNum, ST_NAND_BLK_INFO *pstNandBlk, U32 phyBlkNum)
{
  U08 idx;
  U08 validSectCnt = 0;

  // 遍历读取Page中的各扇区存放的LBA，比对有效性，并格式化掉NAND中不存在，但MAP中存在的LBA记录
  for (idx = 0; idx < SECT_NUM_PER_PAGE; idx++) {
    // 确保Nand记录中的数据有效
    if ((pstNandBlk->sect[pageNum + idx].free == FALSE) &&
        (pstNandBlk->sect[pageNum + idx].valid == TRUE)) {
          // 确保数据一致性
          ASSERT(lsnsArr[idx] == pstNandBlk->sect[pageNum + idx].lsn);
          validSectCnt++;
        } else {
          fprintf (stdout, "Data Unmap: Blk: %d, page %d: Sect:%d, free: %d, valid: %d lsns[%d]: %d\n", 
                   phyBlkNum, pageNum, pageNum + idx, pstNandBlk->sect[pageNum + idx].free, 
                   pstNandBlk->sect[pageNum + idx].valid, idx, lsnsArr[idx]);
          // 格式化掉NAND中不存在，但MAP中存在的LBA记录
          lsnsArr[idx] = -1;
        }
  }
  return validSectCnt;
}

/**
 * @Description: 统计Nand 读操作
 * @param :U08 validSectCnt: 读取的有效Sect数
 *         EN_FLASH_READ_TYPE enType： flash读类型
 * @return {type}  
 * @Author: zhoujie
 * @Date: 2020-08-16 23:56:55
 * @Note: 
 */
void flashReadStatRecord(U08 validSectCnt, EN_FLASH_READ_TYPE enType)
{
  switch (enType)
  {
    case EN_FLASH_NORMAL_READ:
      Flash_NandStat(EN_GC_PAGE_READ);
      break;
    case EN_FLASH_GC_READ:
    case EN_FLASH_GC_FORMAT_READ:
      Flash_NandStat(EN_GC_PAGE_READ);
      break;
    default:
      break;
  }
  return;
}

/**
 * @Description: 读取Nand中的数据
 * @Param:   U32 phySectAddr:读取的物理扇区地址
 *           U32 *lsnArr：需要比对的LBA数组或需要加载，数组长度 默认是SECT_NUM_PER_PAGE
 *           EN_FLASH_READ_TYPE enType:
 *              EN_FLASH_NORMAL_READ 正常数据读取，是模拟比对
 *              EN_FLASH_GC_READ GC数据读取，会将lsnArr地址赋值
 *              EN_FLASH_GC_FORMAT_READ 格式化掉Nand中不存在但MAP中记录的LBA，过程中还会比较数据一致性
 * @Return: validSectCnt:比对成功的有效Sect数/成功加载的Sect数
 * @Author: zhoujie
 * @Date: 2020-08-16 14:13:48
 * @Note: 
 */
U08 Flash_NandPageRead(U32 phySectAddr, U32 *lsnArr, EN_FLASH_READ_TYPE enType)
{
  U08 validSectCnt; 
  // physical block number
  U32 phyBlkNum = BLK_F_SECT(phySectAddr);   
  // page index (within the block), here page index is the same as sector index
  U16 pageOffSet = IND_F_SECT(phySectAddr);  
  ST_NAND_BLK_INFO *pstNandBlk = NULL;

  // debug print
  if (phyBlkNum > g_pstNandGi->allBlkNum) {
    fprintf(stderr, "phySectArr: %d, phyBlkNum %d, Max Blk Num: %d\n", 
            phyBlkNum, phySectAddr, g_pstNandGi->allBlkNum);
    ASSERT(0);
  }

  pstNandBlk = &g_pstNandGi->pstNandBlkInfo[phyBlkNum];
  // Page first Sect is OOB, not Data 
  ASSERT(OFF_F_SECT(phySectAddr) == 0);
  //  block should be written with something
  ASSERT(pstNandBlk->state.free != TRUE);
  
  // 根据模式比对/加载 数据
  if (enType == EN_FLASH_NORMAL_READ) {
    // 正常数据读，只是比对，查验数据是否一致
    validSectCnt = flashNormalRead(lsnArr, pageOffSet, pstNandBlk, phyBlkNum);
  } else if (enType == EN_FLASH_GC_READ) {
    // GC读取，是读取Nand的数据（LBA），返回给MAP控制更新
    validSectCnt = flashGcRead(lsnArr, pageOffSet, pstNandBlk, phyBlkNum);
  } else if (enType == EN_FLASH_GC_FORMAT_READ) {
    // 此模式下比对完数据，同时清除 MAP lsns数组中的无效的LBA （格式化操作）
    validSectCnt = flashFormatRead(lsnArr, pageOffSet, pstNandBlk, phyBlkNum);
  }

  // 统计Nand操作
  flashReadStatRecord(validSectCnt, enType);

  return validSectCnt;
}


/**
 * @Description: 擦除Nand，更新EC，同时初始化NAND对应BLK的控制信息
 * @param U32 blkNum:等待擦除的块号
 * @return void  
 * @Author: zhoujie
 * @Date: 2020-08-17 23:00:03
 * @Note: 
 */
void Flash_NandErase(U32 blkNum)
{
  int idx;
  // check input value is vaild
  ASSERT(blkNum < g_pstNandGi->allBlkNum);

  // initialize/reset blk status
  g_pstNandGi->pstNandBlkInfo[blkNum].freePageCnt = SECT_NUM_PER_BLK;
  g_pstNandGi->pstNandBlkInfo[blkNum].state.free == TRUE;
  g_pstNandGi->pstNandBlkInfo[blkNum].state.ec++;
  g_pstNandGi->pstNandBlkInfo[blkNum].lastWritePageIdx = -1;
  g_pstNandGi->pstNandBlkInfo[blkNum].invalidPageCnt = 0;

  // initialize/reset sect status
  for (idx = 0; idx < SECT_NUM_PER_BLK; idx++) {
    g_pstNandGi->pstNandBlkInfo[blkNum].sect[idx].free = TRUE;
    g_pstNandGi->pstNandBlkInfo[blkNum].sect[idx].valid = FALSE;
    g_pstNandGi->pstNandBlkInfo[blkNum].sect[idx].lsn = -1;
  }

  // initialize/reset page status 
  for (idx = 0; idx < PAGE_NUM_PER_BLK; idx++) {
    g_pstNandGi->pstNandBlkInfo[blkNum].pageStatus[idx] = -1;
  }

  g_pstNandGi->freeBlkNum++;
  Flash_NandStat(EN_NAND_BLOCK_ERASE);
  return;
}

/**
 * @Description:  按Page为粒度无效化掉扇区中的数据
 * @param   U32 phySectAddr：无效化的扇区地址,一般对齐到Page点
 *          U32 lsn:需要无效化掉的‘数据 ’ - lba 
 * @return  void
 * @Author: zhoujie
 * @Date: 2020-08-17 23:46:23
 * @Note: 
 */
void Flash_NandInvalid(U32 phySectAddr, U32 lsn)
{
  // physical block number
  U32 phyBlkNum = BLK_F_SECT(phySectAddr);   
  // page index (within the block), here page index is the same as sector index
  U16 pageOffSet = IND_F_SECT(phySectAddr);  

  ASSERT(phyBlkNum < g_pstNandGi->allBlkNum);
  ASSERT(g_pstNandGi->pstNandBlkInfo[phyBlkNum].sect[pageOffSet].free == FALSE);
  ASSERT(g_pstNandGi->pstNandBlkInfo[phyBlkNum].sect[pageOffSet].valid == TRUE);
  ASSERT(g_pstNandGi->pstNandBlkInfo[phyBlkNum].sect[pageOffSet].lsn == lsn);

  g_pstNandGi->pstNandBlkInfo[phyBlkNum].sect[pageOffSet].valid = FALSE;
  g_pstNandGi->pstNandBlkInfo[phyBlkNum].invalidPageCnt++;
  ASSERT(g_pstNandGi->pstNandBlkInfo[phyBlkNum].invalidPageCnt < SECT_NUM_PER_BLK);
  return;
}

/**
 * @Description:  
 * @param {type}  
 * @return {type}  
 * @Author: zhoujie
 * @Date: 2020-08-17 23:59:56
 * @Note: 
 */
U08 Flash_NandPageWrite(U32 psn, U32 *lsnsArr, EN_NAND_PAGE_READ enType, int mapFlag)
{
  return 0;
}

/**************** NAND PAGE WRITE **********************/
_u8 nand_page_write(_u32 psn, _u32 *lsns, _u8 isGC, int map_flag)
{
  blk_t pbn = BLK_F_SECT(psn);	// physical block number with psn
  _u16  pin = IND_F_SECT(psn);	// sector index, page index is the same as sector index 
  int i, valid_sect_num = 0;


  if(pbn >= nand_blk_num){
    printf("break !\n");
  }

  ASSERT(pbn < nand_blk_num);
  ASSERT(OFF_F_SECT(psn) == 0);

  if(map_flag == 2) {
        nand_blk[pbn].page_status[pin/SECT_NUM_PER_PAGE] = 1; // 1 for map table
  }
  else{
    nand_blk[pbn].page_status[pin/SECT_NUM_PER_PAGE] = 0; // 0 for data 
  }

  for (i = 0; i <SECT_NUM_PER_PAGE; i++) {

    if (lsns[i] != -1) {

      if(nand_blk[pbn].state.free == 1) {
        printf("blk num = %d",pbn);
      }

      ASSERT(nand_blk[pbn].sect[pin + i].free == 1);
      
      nand_blk[pbn].sect[pin + i].free = 0;			
      nand_blk[pbn].sect[pin + i].valid = 1;			
      nand_blk[pbn].sect[pin + i].lsn = lsns[i];	
      nand_blk[pbn].fpc--;  
      nand_blk[pbn].lwn = pin + i;	
      valid_sect_num++;
    }
    else{
      printf("lsns[%d] do not have any lsn\n", i);
    }
  }
  
  ASSERT(nand_blk[pbn].fpc >= 0);

  if (isGC) {
    nand_stat(GC_PAGE_WRITE);
  } else {
    nand_stat(PAGE_WRITE);
  }

  return valid_sect_num;
}

_u32 nand_get_free_blk (int isGC) 
{
  _u32 blk_no = -1, i;
  int flag = 0,flag1=0;
  flag = 0;
  flag1 = 0;

  int INF=9999999;
  MIN_ERASE =INF;

  //in case that there is no avaible free block -> GC should be called !
  if ((isGC == 0) && (min_fb_num >= free_blk_num)) {
//    printf("free blk num is : %d\n", free_blk_num);
    return -1;
  }

  for(i = 0; i < nand_blk_num; i++) 
  {
    if (nand_blk[i].state.free == 1) {
      flag1 = 1;
      //debug print
//      if(i%100==0 && nand_blk[i].state.ec >100000){
//        printf("nand_blk[%d].state.ec is %d\n",i,nand_blk[i].state.ec);
//      }


      if ( nand_blk[i].state.ec < MIN_ERASE ) {
            blk_no = i;
            MIN_ERASE = nand_blk[i].state.ec;
            flag = 1;
      }
    }
  }
//  debug
  if(MIN_ERASE == INF){
    printf ("MIN_ERASE is %d,MAX ERASE is %d \n",MIN_ERASE,INF);
    assert(0);
  }

  if(flag1 != 1){
    printf("no free block left=%d",free_blk_num);
    
  ASSERT(0);
  }
  if ( flag == 1) {
        flag = 0;
        ASSERT(nand_blk[blk_no].fpc == SECT_NUM_PER_BLK);
        ASSERT(nand_blk[blk_no].ipc == 0);
        ASSERT(nand_blk[blk_no].lwn == -1);
        nand_blk[blk_no].state.free = 0;

        free_blk_idx = blk_no;
        free_blk_num--;

        return blk_no;
  }
  else{
    printf("shouldn't reach...\n");
  }

  return -1;
}
